#include <fstream>
#include <sstream>
#include <iostream>
#include "GameInfo.h"
#include "GameMath.h"
#include "../manager/PlayerManager.h"

std::unordered_map<SkillId, int> GameInfo::skillConsume = {
        {SkillId::SKILL_RASENGAN,   0},
        {SkillId::SKILL_DISCHARGE,  0},
        {SkillId::SKILL_RASENSHURIKEN, 20},
        {SkillId::SKILL_CHIDORI, 20},
        {SkillId::SKILL_PURPLE_ENERGY, 20},
        {SkillId::SKILL_WHIRL_WIND, 40},
};

bool GameInfo::LoadFromFile()
{
    if (!ParseStaticFile()) {
        return false;
    }
    ParseCharacter();
    return true;
}

bool GameInfo::ParseStaticFile()
{
    std::ifstream file("config/game_file_path.json");
    if (!file.good()) {
        return false;
    }
    std::stringstream sinStr;
    sinStr << file.rdbuf();
    file.close();
    json root = json::parse(sinStr.str());
    if (root.empty()) {
        return false;
    }
    if (!ParseTex(root) || !ParseMix(root) || !ParseMusic(root)) {
        return false;
    }
    if ((font = TTF_OpenFont("resources/font/ipix.ttf", 25)) == nullptr) {
        return false;
    }
    return true;
}

bool GameInfo::ParseTex(const json &root)
{
    json textureFilePath = root["TexPath"];
    auto id = (TexId)(TexId::TEX_BEGIN + 1);
    for (auto &filePath: textureFilePath) {
        std::string str = to_string(filePath);
        str = str.substr(1, str.size() - 2);
        texturePool[id] = IMG_LoadTexture(renderer, str.c_str());
        if (texturePool[id] == nullptr) {
            std::cout << "file: " + str + " is loss." << std::endl;
            return false;
        }
        id = (TexId)(id + 1);
        if (id == TexId::TEX_END) {
            break;
        }
    }

    return true;
}

bool GameInfo::ParseMix(const json &root){
    json mixFilePath = root["MixPath"];
    auto id = (MixId)(MixId::MIX_BEGIN + 1);
    for (auto &filePath: mixFilePath) {
        std::string str = to_string(filePath);
        str = str.substr(1, str.size() - 2);
        mixPool[id] = Mix_LoadWAV(str.c_str());
        if (mixPool[id] == nullptr) {
            std::cout << "file: " + str + " is loss." << std::endl;
            return false;
        }
        id = (MixId)(id + 1);
        if (id == MixId::MIX_END) break;
    }
    return true;
}

bool GameInfo::ParseMusic(const json &root)
{
    json musicFilePath = root["MusicPath"];
    auto id = (MusicId)(MusicId::MUSIC_BEGIN + 1);
    for (auto &filePath: musicFilePath) {
        std::string str = to_string(filePath);
        str = str.substr(1, str.size() - 2);
        musicPool[id] = Mix_LoadMUS(str.c_str());
        if (musicPool[id] == nullptr) {
            std::cout << "file: " + str + " is loss." << std::endl;
            return false;
        }
        id = (MusicId)(id + 1);
        if (id == MusicId::MUSIC_END) break;
    }
    return true;
}

bool GameInfo::ParseCharacter()
{
    std::ifstream file("config/character.json");
    if (!file.good()) {
        return false;
    }
    std::stringstream sinStr;
    sinStr << file.rdbuf();
    file.close();
    json root = json::parse(sinStr.str());
    if (root.empty()) {
        return false;
    }
    if (!ParseHero(root)) {
        return false;
    }
    return true;
}

bool GameInfo::ParseHero(const json &root)
{
    json heroJson = root["Hero"];
    for (auto &hero: heroJson) {
        HeroInfo info{};
        info.characterId =  std::stoi(to_string(hero["CharacterId"]));
        info.speed =  std::stoi(hero["speed"].dump());
        info.maxSpeed =  std::stoi(hero["maxSpeed"].dump());
        info.hp =  std::stoi(hero["hp"].dump());
        info.maxHp =  std::stoi(hero["maxHp"].dump());
        info.mp =  std::stoi(hero["mp"].dump());
        info.maxMp =  std::stoi(hero["maxMp"].dump());
        info.recoverMp =  std::stoi(hero["recoverMp"].dump());
        info.recoverHp =  std::stoi(hero["recoverHp"].dump());
        info.width =  std::stoi(hero["width"].dump());
        info.height =  std::stoi(hero["height"].dump());
        info.attackWidth =  std::stoi(hero["attackWidth"].dump());
        info.attackHeight =  std::stoi(hero["attackHeight"].dump());
        info.damage =  std::stoi(hero["damage"].dump());
        if (info.hp < 0 || info.maxHp <= 0) {
            info.hp = 0, info.maxHp = 0;
        }
        heroInfoPool[(CharacterId)info.characterId] = info;
    }
    return true;
}

bool GameInfo::CheckCollision(Object *object)
{
    for (auto &enemy: GameInfo::Instance().enemies) {
        if (object == enemy.get()) continue;
        if (GameMath::CollisionDetection(object->position, object->GetShape(), enemy->position, enemy->GetShape())) {
            return true;
        }
    }
    auto player = PlayerManager::Instance().GetPlayer();
    if (object != player.get() &&
        GameMath::CollisionDetection(object->position, object->GetShape(), player->position, player->GetShape())) {
        return true;
    }
    return false;
}

void GameInfo::InMap(GameVector &position) const
{
    map.InMap(position);
}